<?php
declare(strict_types=1);

namespace app\service;

use think\exception\ValidateException;

/**
 * 加密服务类
 * 提供AES加密解密和签名验证功能
 */
class CryptoService
{
    // 加密配置
    private $config = [
        'aes_key' => 'travelmeow123s@456',  // 16位密钥
        'aes_iv' => '1234567890123456',   // 16位初始向量
        'sign_key' => 'travel_meow_sign_key_2024',
        'enabled' => true,
        'timestamp_tolerance' => 600  // 10分钟时间容差
    ];
    
    public function __construct()
    {
        // 从配置文件读取加密配置
        $cryptoConfig = config('crypto', []);
        if (!empty($cryptoConfig)) {
            $this->config = array_merge($this->config, $cryptoConfig);
        }
    }
    
    /**
     * AES加密
     */
    public function aesEncrypt(string $plaintext, ?string $key = null, ?string $iv = null): string
    {
        $key = $key ?: $this->config['aes_key'];
        $iv = $iv ?: $this->config['aes_iv'];
        
        // PKCS7填充
        $blockSize = 16;
        $padding = $blockSize - (strlen($plaintext) % $blockSize);
        $plaintext .= str_repeat(chr($padding), $padding);
        
        // 简单的异或加密（与前端对应）
        $encrypted = '';
        for ($i = 0; $i < strlen($plaintext); $i++) {
            $keyChar = ord($key[$i % strlen($key)]);
            $ivChar = ord($iv[$i % strlen($iv)]);
            $plainChar = ord($plaintext[$i]);
            $encrypted .= chr($plainChar ^ $keyChar ^ $ivChar);
        }
        
        return base64_encode($encrypted);
    }
    
    /**
     * AES解密
     */
    public function aesDecrypt(string $ciphertext, ?string $key = null, ?string $iv = null): string
    {
        $key = $key ?: $this->config['aes_key'];
        $iv = $iv ?: $this->config['aes_iv'];
        
        try {
            $encrypted = base64_decode($ciphertext);
            $decrypted = '';
            
            // 简单的异或解密
            for ($i = 0; $i < strlen($encrypted); $i++) {
                $keyChar = ord($key[$i % strlen($key)]);
                $ivChar = ord($iv[$i % strlen($iv)]);
                $cipherChar = ord($encrypted[$i]);
                $decrypted .= chr($cipherChar ^ $keyChar ^ $ivChar);
            }
            
            // 移除PKCS7填充
            $padding = ord($decrypted[strlen($decrypted) - 1]);
            return substr($decrypted, 0, strlen($decrypted) - $padding);
        } catch (\Exception $e) {
            throw new ValidateException('数据解密失败: ' . $e->getMessage());
        }
    }
    
    /**
     * 生成签名
     */
    public function generateSign($data, int $timestamp, string $nonce, ?string $signKey = null): string
    {
        $signKey = $signKey ?: $this->config['sign_key'];
        // 构建签名参数
        $params = [
            'data' => is_string($data) ? $data : json_encode($data),
            'timestamp' => (string)$timestamp,
            'nonce' => $nonce,
            'key' => $signKey
        ];
        
        // 按字母顺序排序
        ksort($params);
        
        // 构建签名字符串，与前端保持一致
        $signParts = [];
        foreach ($params as $key => $value) {
            $signParts[] = $key . '=' . $value;
        }
        $signString = implode('&', $signParts);
        
        // 简单的哈希算法（与前端对应）
        $hash = 0;
        for ($i = 0; $i < strlen($signString); $i++) {
            $char = ord($signString[$i]);
            $hash = (($hash << 5) - $hash) + $char;
            $hash = $hash & 0xFFFFFFFF; // 转换为32位整数，确保不会溢出
        }
        
        return dechex(abs($hash));
    }
    
    /**
     * 验证签名
     */
    public function verifySign($data, int $timestamp, string $nonce, string $sign): bool
    {
        if (!$this->config['enabled']) {
            return true;
        }
        
        try {
            // 验证时间戳
            $currentTime = time();
            if (abs($currentTime - $timestamp) > $this->config['timestamp_tolerance']) {
                return false;
            }
            
            // 生成期望的签名
            $expectedSign = $this->generateSign($data, $timestamp, $nonce);
            return $expectedSign === $sign;
        } catch (\Exception $e) {
            trace('签名验证失败: ' . $e->getMessage(), 'error');
            return false;
        }
    }
    
    /**
     * 解密请求数据
     */
    public function decryptRequest(array $requestData): array
    {
        if (!$this->config['enabled']) {
            return $requestData;
        }
        
        // 检查是否为加密请求
        if (!isset($requestData['data'], $requestData['timestamp'], $requestData['nonce'], $requestData['sign'])) {
            return $requestData;
        }
        
        try {
            // 解密数据
            $decryptedData = $this->aesDecrypt($requestData['data']);
            
            // 验证签名 - 使用解密后的数据进行验证
            if (!$this->verifySign($decryptedData, $requestData['timestamp'], $requestData['nonce'], $requestData['sign'])) {
                throw new ValidateException('请求签名验证失败');
            }
            
            // 尝试解析JSON
            $jsonData = json_decode($decryptedData, true);
            return $jsonData !== null ? $jsonData : ['data' => $decryptedData];
            
        } catch (\Exception $e) {
            throw new ValidateException('请求解密失败: ' . $e->getMessage());
        }
    }
    
    /**
     * 加密响应数据
     */
    public function encryptResponse($responseData): array
    {
        if (!$this->config['enabled']) {
            return is_array($responseData) ? $responseData : ['data' => $responseData];
        }
        
        try {
            $timestamp = time();
            $nonce = $this->generateNonce();
            $dataStr = is_string($responseData) ? $responseData : json_encode($responseData);
            
            // 生成签名 - 使用原始数据生成签名，与前端保持一致
            $sign = $this->generateSign($dataStr, $timestamp, $nonce);
            
            // 加密数据
            $encryptedData = $this->aesEncrypt($dataStr);
            
            return [
                'data' => $encryptedData,
                'timestamp' => $timestamp,
                'nonce' => $nonce,
                'sign' => $sign
            ];
        } catch (\Exception $e) {
            throw new ValidateException('响应加密失败: ' . $e->getMessage());
        }
    }
    
    /**
     * 生成随机字符串
     */
    public function generateNonce(int $length = 16): string
    {
        $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
        $nonce = '';
        for ($i = 0; $i < $length; $i++) {
            $nonce .= $chars[rand(0, strlen($chars) - 1)];
        }
        return $nonce;
    }
    
    /**
     * 检查是否启用加密
     */
    public function isEncryptionEnabled(): bool
    {
        return $this->config['enabled'];
    }
    
    /**
     * 设置加密配置
     */
    public function setConfig(array $config): void
    {
        $this->config = array_merge($this->config, $config);
    }
    
    /**
     * 获取加密配置
     */
    public function getConfig(): array
    {
        return $this->config;
    }
}